#pragma once
#include"Util.hpp"
#include "Reactor.hpp"
#define AE_SETSIZE (1024 * 10)

#define AE_NONE 0
#define AE_READABLE 1
#define AE_WRITEABLE 2
#define AE_FILE_EVENT (AE_READABLE | AE_WRITEABLE)

class aeEvents;

struct aeFiledEvent
{
  int fd = -1;
  int readyEv = 0; // 已就绪事件
};
typedef void aeFileProc(aeEvents *aeEvt, int fd, void *pridata);
struct aeRegisteredEvents
{
  int registerdEv = 0; // 已注册的文件事件
  aeFileProc *readProc = nullptr;
  aeFileProc *writeProc = nullptr;
  void *pridata;
};
//时间事件相关结构
typedef int aeTimeProc();
//时间事件节点
struct aeTimeEvent
{
  long long id;
  long when_sec;
  long when_ms;//精确到毫秒
  aeTimeProc* timeProc;
};
class aeEvents
{
private:
  Reactor _reactor;
  aeRegisteredEvents _registeredEvs[AE_SETSIZE]; // 数组下表代表fd，包含了已就绪文件事件的处理方式
  aeFiledEvent _filedEvs[AE_SETSIZE];            // 已就绪的文件事件
  std::list<aeTimeEvent> _timeEvents;
public:
  aeEvents() : _reactor()
  {
  }
  /****文件事件****/
  void registerTimeEvent(long when_sec,long when_ms,aeTimeProc* timeProc)
  {
    aeTimeEvent timeEvents;
    timeEvents.when_sec = when_sec;
    timeEvents.when_ms = when_ms;
    timeEvents.timeProc = timeProc;
    if(_timeEvents.empty())
    timeEvents.id = 0;
    else
    timeEvents.id = _timeEvents.back().id+1;

    _timeEvents.push_back(timeEvents);
  }
  long  getNearestTime()
  {
    aeTimeEvent* p_timeEv=&_timeEvents.front();

    for(auto& e : _timeEvents)
    {
      if(p_timeEv->when_ms > e.when_sec || (p_timeEv->when_sec==e.when_sec && p_timeEv->when_sec>e.when_ms))
      {
        p_timeEv = &e;
      }
    }
    int gap = p_timeEv->when_sec + p_timeEv->when_ms==0 ? 0 : p_timeEv->when_ms/1000+1  -  time(nullptr);
    return gap > 0 ? gap : 0;
  }
  void processTimeEvents()
  {
     long now_sec,now_ms;
     aeGetTime(now_sec,now_ms);

     auto begin = _timeEvents.begin();
     while(begin!=_timeEvents.end())
     {
      //时间到了
       if(now_sec>begin->when_sec ||(now_sec==begin->when_sec&&now_ms>=begin->when_ms))
       {
        long ms2 = begin->timeProc();
        //定时时间
        if(ms2==0)
         begin = _timeEvents.erase(begin);
        else
        {
           begin->when_sec = now_sec;
           begin->when_ms = now_ms;
           aeAddTime(begin->when_sec,begin->when_ms,ms2);  
           begin++;
        }
       }
       else
       begin++;
     }
  }
  int getFiledEvents(timeval *tv)
  {
    epoll_event evs[AE_SETSIZE];
    int num_events = _reactor.waitUtilEvReady(evs, AE_SETSIZE, tv);
    
    if (num_events < 0)
      return AE_ERR;
    // 将就绪事件放到aeFiledEvent数组中去
    memset(_filedEvs, 0, sizeof(_filedEvs));
    for (int i = 0; i < num_events; i++)
    {
      _filedEvs[i].fd = evs[i].data.fd;
      if (evs[i].events & EPOLLIN)
      {
        _filedEvs[i].readyEv |= AE_READABLE;
        //cout<<"有可读文件事件就绪"<<endl;
      }
      if (evs[i].events & EPOLLOUT) 
      {
        _filedEvs[i].readyEv |= AE_WRITEABLE;
        //cout<<"有可写文件事件就绪"<<endl;
      }
      if (evs[i].events & EPOLLERR) // 将出错处理归到可读可写事件中
      {
        _filedEvs[i].readyEv |= AE_FILE_EVENT;
        //cout<<"有出错文件事件就绪"<<endl;
      }
    }
    if (num_events < AE_SETSIZE)
      _filedEvs[num_events].fd = -1;
  }

  void processFiledEvents()
  {
    int i = 0;
    while (i < AE_SETSIZE && _filedEvs[i].fd != -1)
    {
      int fd = _filedEvs[i].fd;
      if ((_filedEvs[i].readyEv & AE_READABLE &_registeredEvs[fd].registerdEv)  && _registeredEvs[fd].readProc)
          _registeredEvs[fd].readProc(this, fd, _registeredEvs[fd].pridata);
        

      if ((_filedEvs[i].readyEv & AE_WRITEABLE &_registeredEvs[fd].registerdEv) && _registeredEvs[fd].writeProc&& IsVolidFd(fd)) // 确保文件描述符没有被关闭
        _registeredEvs[fd].writeProc(this, fd, _registeredEvs[fd].pridata);

      i++;
    }
  }

  bool IsVolidFd(int fd)
  {
    return _registeredEvs[fd].registerdEv ? true:false;
  }

  int getFdEvents(int fd)
  {
    return _registeredEvs[fd].registerdEv;
  }

  bool registeFileEvent(int fd, int events, void *pridata, aeFileProc *readProc = nullptr, aeFileProc *writeProc = nullptr)
  {
    //if(events & AE_READABLE) cout<<"addFileEvent AE_READABLE"<<endl;
    //if(events & AE_WRITEABLE) cout<<"addFileEvent AE_WRITEABLE"<<endl;
    assert(fd >= 0 && fd < AE_SETSIZE);
    // 添加至epoll模型中
    epoll_event ev;
    memset(&ev, 0, sizeof(ev));
    ev.data.fd = fd;
    if (events & AE_READABLE)
      ev.events |= EPOLLIN;
    if (events & AE_WRITEABLE)
      ev.events |= EPOLLOUT;
    if (_reactor.modifyEv(fd, EPOLL_CTL_ADD, &ev) == false)
      return false;

    // 添加给aeEvents
    _registeredEvs[fd].registerdEv = events;
    _registeredEvs[fd].readProc = readProc;
    _registeredEvs[fd].writeProc = writeProc;
    _registeredEvs[fd].pridata = pridata;
  }

  //
  bool addFileEvent(int fd, int events,aeFileProc* readProc=nullptr,aeFileProc* writeProc=nullptr)
  {
    //if(events & AE_READABLE) cout<<"addFileEvent AE_READABLE"<<endl;
    //if(events & AE_WRITEABLE) cout<<"addFileEvent AE_WRITEABLE"<<endl;
    assert(fd >= 0 && fd < AE_SETSIZE);

    auto &registerdEv = _registeredEvs[fd].registerdEv;
    registerdEv |= events;

    epoll_event ev;
    memset(&ev, 0, sizeof(ev));
    ev.data.fd=fd;
    if (registerdEv & AE_READABLE)
      ev.events |= EPOLLIN;
    if (registerdEv & AE_WRITEABLE)
      ev.events |= EPOLLOUT;
    if(_registeredEvs[fd].readProc==nullptr && readProc!=nullptr) _registeredEvs[fd].readProc=readProc;
    if(_registeredEvs[fd].writeProc==nullptr && writeProc!=nullptr) _registeredEvs[fd].writeProc=writeProc;

    return _reactor.modifyEv(fd, EPOLL_CTL_MOD, &ev);
  }

  bool deleteFileEvent(int fd, int events = AE_FILE_EVENT)
  {
    //if(events & AE_READABLE) cout<<"deleteFileEvent AE_READABLE"<<endl;
    //if(events & AE_WRITEABLE) cout<<"deleteFileEvent AE_WRITEABLE"<<endl;
    assert(fd >= 0 && fd < AE_SETSIZE);
    auto &registerdEv = _registeredEvs[fd].registerdEv;

    registerdEv &= ~events;

    if (registerdEv == 0)
    {
        return _reactor.modifyEv(fd, EPOLL_CTL_DEL, nullptr);
    }
    else
    {
      epoll_event ev;
      memset(&ev,0,sizeof(ev));
      ev.data.fd=fd;
      if (registerdEv & AE_READABLE)
        ev.events |= EPOLLIN;
      if (registerdEv & AE_WRITEABLE)
        ev.events |= EPOLLOUT;
      return _reactor.modifyEv(fd, EPOLL_CTL_MOD, &ev);
    }
  }
  /**************************************/
};